﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting;
using ServerLibForClient;

namespace ServerConsole
{
    public class Server : MarshalByRefObject
    {
        enum MessageType : byte
        {
            GetFreeHost = 1, // Запросить свободные хосты
            GetSerchAgents = 2 // Запросить агентов с задачей
        }

        //public event ReciveCommantGetFreeHostEventHandler ReciveCommantGetFreeHostEvent;
        //public delegate void ReciveCommantGetFreeHostEventHandler(string host, string port, string searchRequestHash);

        public event ReciveCommantGetSerchAgentsEventHandler SearchRequestEvent;
        public delegate void ReciveCommantGetSerchAgentsEventHandler(string host, string port, string searchRequestHash);

        string hostName = "";
        public string HostName
        {
            get { return hostName; }
            set { hostName = value; }
        }

        int agentsLimit = 4;
        public int AgentsLimit
        {
            get { return agentsLimit; }
        }

        List<string> searchRequestHashs = new List<string>();
        List<Agent> agents = new List<Agent>();
        int RemServerPort = 780;
        int udpServerPort = 781;
        string hostIP;
        Thread workThread;
        private bool isRemoutingInit = false;
        UdpClient udpClient;
        bool isStoped = false;
        List<string> TaskList = new List<string>();
        /// <summary>
        /// флаг сервер занят
        /// </summary>
        bool isBusy = false;

        public string baseFolger = @"C:\Temp";

        public string FreeHost = "";

        public Server()
        {
            ServerLibForClient.SearchRequest.SearchRequestEvent += new ServerLibForClient.SearchRequest.SearchRequestEventHandler(SearchRequest_SearchRequestEvent);
            workThread = new Thread(doWork);
        }

        void SearchRequest_SearchRequestEvent(ServerLibForClient.SearchRequestStruct searchRequestData)
        {
            // Создание агента и запуск
            isBusy = true;
            Agent newAgent = new Agent(searchRequestData, this);
            newAgent.Start();
            TaskList.Add(searchRequestData.Sha1Hash);
            agents.Add(newAgent); //! Синхронизация
        }

        ~Server()
        {
            // Прерывание потока
            if (workThread.IsAlive)
                workThread.Abort();
            if (udpClient != null)
                udpClient.Close();
        }

        /// <summary>
        /// Запуск сервера
        /// </summary>
        public void Start()
        {
            initRemouting();
            ServersRem.GetServerEvent += new ServersRem.GetServerEventHandler(ServersRem_GetServerEvent);
            ServersRem.ReciveFreeHostEvent += new ServersRem.ReciveFreeHostEventHandler(ServersRem_ReciveFreeHostEvent);
            if (!workThread.IsAlive)
                workThread.Start();
        }

        void ServersRem_ReciveFreeHostEvent(string host, int port)
        {
            lock (FreeHost)
            {
                FreeHost = host + ":" + port;
            }
        }

        Server ServersRem_GetServerEvent()
        {
            return this;
        }

        void doWork()
        {
            Console.WriteLine("Инициализация UDP сокета...");
            try
            {
                udpClient = new UdpClient(udpServerPort);
                IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Broadcast, udpServerPort);
                Console.WriteLine(ipEndPoint.ToString());
                while (!isStoped)
                {
                    byte[] receiveBytes = udpClient.Receive(ref ipEndPoint);
                    Console.WriteLine("UDP пакет:");

                    for (int i = 0; i < receiveBytes.Length; i++)
                    {
                        Console.Write(receiveBytes[i]);
                    }
                    Console.WriteLine();



                    MessageType t = (MessageType)receiveBytes[0];
                    switch (t)
                    {
                        case MessageType.GetFreeHost:
                            string TaskHash = Encoding.ASCII.GetString(receiveBytes, 1, 28);
                            Console.WriteLine("TaskHash: " + TaskHash);
                            if (!CheckTask(TaskHash))
                            {
                                string RemAdr = Encoding.ASCII.GetString(receiveBytes, 29, 18);
                                RemAdr = RemAdr.Replace("\0", "");
                                Console.WriteLine("Хост свободен. адресс агента:" + RemAdr);
                                ServersRem ServersRemProxy = (ServersRem)Activator.GetObject(typeof(ServersRem), "tcp://" + RemAdr + "/ServersRem.soap");
                                ServersRemProxy.ReciveFreeHost(Settings1.Default.HostRemAdr, RemServerPort);
                            }
                            break;
                        case MessageType.GetSerchAgents:
                            break;
                        default:
                            break;
                    }
                }
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                udpClient.Close();
            }

            Console.WriteLine("UDP закрыт");
        }

        public bool GetFreeHosts(string hash)
        {
            if (udpClient != null)
            {
                byte[] messageBuff = new byte[1+hash.Length+18];
                messageBuff[0] = (byte)MessageType.GetFreeHost;
                Array.Copy(Encoding.ASCII.GetBytes(hash), 0, messageBuff, 1, hash.Length);
                //messageBuff.CopyTo(Encoding.ASCII.GetBytes(hash),1);
                string host = Settings1.Default.HostRemAdr + ":" + RemServerPort;
                Array.Copy(Encoding.ASCII.GetBytes(host), 0, messageBuff, 1 + hash.Length, host.Length);
                //messageBuff.CopyTo(Encoding.ASCII.GetBytes(Settings1.Default.HostRemAdr + ":" + RemServerPort), 1+hash.Length);

                IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Broadcast, udpServerPort);
                udpClient.Send(messageBuff, messageBuff.Length, ipEndPoint);
            }
            return true;
        }

        /// <summary>
        /// Инициализация сервера ремоутинга
        /// </summary>
        /// <returns>Результат инициализации</returns>
        bool initRemouting()
        {
            if (!isRemoutingInit)
            {
                TcpServerChannel server_channel = new TcpServerChannel("ServerChanel", RemServerPort);
                ChannelServices.RegisterChannel(server_channel, false);

                RemotingConfiguration.RegisterWellKnownServiceType(typeof(SearchRequest), "SearchRequest.soap", WellKnownObjectMode.SingleCall);
                RemotingConfiguration.RegisterWellKnownServiceType(typeof(ServersRem), "ServersRem.soap", WellKnownObjectMode.Singleton);
            }
            return true;
        }

        internal void Stop()
        {
            isStoped = true;

            if (udpClient != null)
                udpClient.Close();
        }

        bool CheckTask(string sha1Task)
        {
            return TaskList.Contains(sha1Task);
        }
    }
}
